#include <iostream>
#include <string>
#include <sstream>
#include "zmq.hpp"
#include "json.h"
#include "Main.h"
#include "IO_Functions.h"
#include "Tank_Info_Functions.h"
#include "Tank_Action_Functions.h"
#include "Map_Functions.h"

using namespace std;

// Constants
const double pi = 3.14159265359;

int main()
{
	// Initialize zmq
	zmq::context_t context(1); // Create the IO thread
	zmq::socket_t state_channel(context, ZMQ_SUB); // Subscriber channel connection
	zmq::socket_t command_channel(context, ZMQ_REQ); // Command channel connection

	// Declare variables
	string ip; // Server address
	string match_token; // Secret password to log in to a battle and receive game state messages
	string client_token; // Secret password to issue tank commands
	json::Object game_state; // Game state information

	// More variables
	vector<double> enemy_data(13); // Enemy velocity data

	// Initial setup
	ip = prompt_for_server_ip(); // Prompt user for server IP address
	match_token = prompt_for_match_token(); // Prompt user for the match token
	connect_to_server(command_channel, state_channel, match_token, ip); // Connect to all channels and send Match Connect message
	client_token = receive_client_token(command_channel); // Receive the client token
	game_state = update_game_state(state_channel); // Wait for the initial game state broadcast, this signals battle start!

	// Main Loop
	while (true) // Infinite loop
	{
		// Check for game start and end message within the game state
		game_state = check_for_game_message(state_channel, game_state);
		// When joining a battle part-way
		if (get_current_map_name() == " ")
		{
			set_current_map_name(determine_current_map_name(game_state));
		}
		// Enemy name 
		if (get_enemy_name() == " ")
		{
			set_enemy_name(game_state);
		}
		// Calculate enemy tank velocity
		calculate_enemy_tank_velocity(game_state, enemy_data);

		// Fast tank logic	
		if (is_our_fast_tank_alive(game_state))
		{
			if (is_enemy_fast_tank_alive(game_state) || is_enemy_slow_tank_alive(game_state))
			{
				if (our_fast_tank_attack_best_target(command_channel, game_state, client_token, enemy_data))
				{
					if (!our_fast_tank_dodge(command_channel, game_state, client_token))
					{
						our_fast_tank_follow_path(command_channel, game_state, client_token);
					}
				}
				else
				{
					if (!our_fast_tank_dodge(command_channel, game_state, client_token))
					{
						our_fast_tank_follow_path(command_channel, game_state, client_token);
					}
				}
			}
			else
			{
				our_fast_tank_stop(command_channel, game_state, client_token, "FIRE");
				if (!our_fast_tank_dodge(command_channel, game_state, client_token))
				{
					our_fast_tank_follow_path(command_channel, game_state, client_token);
				}
			}
		}
		else
		{
			// The tank is dead, when it respawns make it find its way towards the nearest path index
			set_our_fast_tank_path_index(-1);
		}

		// Slow tank logic
		if (is_our_slow_tank_alive(game_state))
		{
			if (is_enemy_fast_tank_alive(game_state) || is_enemy_slow_tank_alive(game_state))
			{
				if (our_slow_tank_attack_best_target(command_channel, game_state, client_token, enemy_data))
				{
					if (!our_slow_tank_dodge(command_channel, game_state, client_token))
					{
						our_slow_tank_follow_path(command_channel, game_state, client_token);
					}
				}
				else
				{
					if (!our_slow_tank_dodge(command_channel, game_state, client_token))
					{
						our_slow_tank_follow_path(command_channel, game_state, client_token);
					}
				}
			}
			else
			{
				our_slow_tank_stop(command_channel, game_state, client_token, "FIRE");
				if (!our_slow_tank_dodge(command_channel, game_state, client_token))
				{
					our_slow_tank_follow_path(command_channel, game_state, client_token);
				}
			}
		}
		else
		{
			// The tank is dead, when it respawns make it find its way towards the nearest path index
			set_our_slow_tank_path_index(-1);
		}
		// Update game state, positions of tanks, health, etc
		game_state = update_game_state(state_channel);
	}
	// Terminate
	return 0;
}

json::Object check_for_game_message(zmq::socket_t& state_channel, json::Object game_state)
{
	// Check if game_state is a game message and if so log the message and update the game_state
	if (game_state["comm_type"] == "GAME_START")
	{
		cout << "Battle Start!\n";
		game_state = update_game_state(state_channel);

		set_enemy_name(game_state);

		// Set the current map name
		set_current_map_name(determine_current_map_name(game_state));

		// Reset path indexes to make the tanks travel to the nearest index
		set_our_fast_tank_path_index(-1);
		set_our_slow_tank_path_index(-1);
	}
	if (game_state["comm_type"] == "GAME_END")
	{
		cout << "Battle end, the winner was: " << (string)game_state["winner"] << "\n";
		game_state = update_game_state(state_channel);
		game_state = check_for_game_message(state_channel, game_state);
	}
	if (game_state["comm_type"] == "MatchEnd")
	{
		cout << "Match End!\n";
		exit(0);
	}
	return game_state;
}

double calculate_distance_between_two_coordinates(double x1, double y1, double x2, double y2)
{
	return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}

double calculate_relative_radial_position(double our_x, double our_y, double other_x, double other_y)
{
	// Move to origin
	double x = other_x - our_x;
	double y = other_y - our_y;

	// Prevent division by 0
	if (x == 0 && y == 0)
	{
		y = 1;
	}

	// Calculate
	double other_radial_position = acos(x / sqrt(pow(x, 2) + pow(y, 2))) / pi * 180;

	// Adjust for the 180 degree gap
	if (other_y < our_y)
	{
		other_radial_position = 360 - other_radial_position;
	}

	return other_radial_position; // Degrees
}

double calculate_optimal_rotation(double our_current_direction, double our_new_direction)
{
	// Fix pason bug.
	if (our_current_direction > 360)
	{
		our_current_direction = our_current_direction - 360;
	}

	// Calculate rotation distance and optimal turning direction
	double turn_right_distance, turn_left_distance;

	// Turn right
	for (double i = 0; true; i += 0.01)
	{
		if (our_current_direction - i < 0)
		{
			if (abs(360 + our_current_direction - i - our_new_direction) <= 0.01)
			{
				turn_right_distance = i;
				break;
			}
		}
		else
		{
			if (abs(our_current_direction - i - our_new_direction) <= 0.01)
			{
				turn_right_distance = i;
				break;
			}
		}
	}

	// Turn left
	for (double i = 0; true; i -= 0.01)
	{
		if (our_current_direction - i > 360)
		{
			if (abs(our_current_direction - i - 360 - our_new_direction) <= 0.01)
			{
				turn_left_distance = i;
				break;
			}
		}
		else
		{
			if (abs(our_current_direction - i - our_new_direction) <= 0.01)
			{
				turn_left_distance = i;
				break;
			}
		}
	}

	// Return the decision
	if (turn_right_distance < abs(turn_left_distance))
	{
		return turn_right_distance;
	}
	else
	{
		return turn_left_distance;
	}
}

double fix_angle(double angle)
{
	if (angle > 360)
	{
		angle = angle - 360;
	}
	else if (angle < 0)
	{
		angle = 360 + angle;
	}

	return angle;
}

stringstream determine_coordinates_of_best_target(json::Object game_state, double our_x, double our_y)
{
	// Variables
	stringstream target;

	bool enemy_fast_alive = is_enemy_fast_tank_alive(game_state);
	bool enemy_slow_alive = is_enemy_slow_tank_alive(game_state);

	if (enemy_fast_alive && !enemy_slow_alive)
	{
		double target_x, target_y;
		enemy_fast_tank_position(game_state) >> target_x >> target_y;

		target << target_x << " " << target_y << " fast";

		if (is_trajectory_blocked(our_x, our_y, target_x, target_y))
		{
			target << " point";
		}
		else
		{
			target << " shoot";
		}
	}
	else if (enemy_slow_alive && !enemy_fast_alive)
	{
		double target_x, target_y;
		enemy_slow_tank_position(game_state) >> target_x >> target_y;

		target << target_x << " " << target_y << " slow";

		if (is_trajectory_blocked(our_x, our_y, target_x, target_y))
		{
			target << " point";
		}
		else
		{
			target << " shoot";
		}
	}
	else if (enemy_slow_alive && enemy_fast_alive)
	{
		double enemy_fast_x, enemy_fast_y, enemy_slow_x, enemy_slow_y;
		enemy_fast_tank_position(game_state) >> enemy_fast_x >> enemy_fast_y;
		enemy_slow_tank_position(game_state) >> enemy_slow_x >> enemy_slow_y;

		bool is_enemy_fast_blocked = is_trajectory_blocked(our_x, our_y, enemy_fast_x, enemy_fast_y);
		bool is_enemy_slow_blocked = is_trajectory_blocked(our_x, our_y, enemy_slow_x, enemy_slow_y);

		if (!is_enemy_fast_blocked && is_enemy_slow_blocked)
		{
			target << enemy_fast_x << " " << enemy_fast_y << " fast" << " shoot";
		}
		else if (!is_enemy_slow_blocked && is_enemy_fast_blocked)
		{
			target << enemy_slow_x << " " << enemy_slow_y << " slow" << " shoot";
		}
		else if (is_enemy_fast_blocked && is_enemy_slow_blocked)
		{
			double distance_to_enemy_fast_tank = calculate_distance_between_two_coordinates(our_x, our_y, enemy_fast_x, enemy_fast_y);
			double distance_to_enemy_slow_tank = calculate_distance_between_two_coordinates(our_x, our_y, enemy_slow_x, enemy_slow_y);

			if (distance_to_enemy_fast_tank < distance_to_enemy_slow_tank)
			{
				target << enemy_fast_x << " " << enemy_fast_y << " fast" << " point";
			}
			else
			{
				target << enemy_slow_x << " " << enemy_slow_y << " slow" << " point";
			}
		}
		else if (!is_enemy_fast_blocked && !is_enemy_slow_blocked)
		{
			double distance_to_enemy_fast_tank = calculate_distance_between_two_coordinates(our_x, our_y, enemy_fast_x, enemy_fast_y);
			double distance_to_enemy_slow_tank = calculate_distance_between_two_coordinates(our_x, our_y, enemy_slow_x, enemy_slow_y);

			if (distance_to_enemy_fast_tank < distance_to_enemy_slow_tank)
			{
				target << enemy_fast_x << " " << enemy_fast_y << " fast" << " shoot";
			}
			else
			{
				target << enemy_slow_x << " " << enemy_slow_y << " slow" << " shoot";
			}
		}
	}
	return target;
}

void calculate_enemy_tank_velocity(json::Object game_state, vector<double>& enemy_data)
{
	double enemy_fast_x = 0, enemy_fast_y = 0, enemy_slow_x = 0, enemy_slow_y = 0;
	if (is_enemy_fast_tank_alive(game_state))
	{
		enemy_fast_tank_position(game_state) >> enemy_fast_x >> enemy_fast_y;
	}

	if (is_enemy_slow_tank_alive(game_state))
	{
		enemy_slow_tank_position(game_state) >> enemy_slow_x >> enemy_slow_y;
	}

	double current_time = game_state["timestamp"];
	double data_time = enemy_data[0];
	double data_enemy_fast_x = enemy_data[1];
	double data_enemy_fast_y = enemy_data[2];
	double data_enemy_slow_x = enemy_data[3];
	double data_enemy_slow_y = enemy_data[4];

	// Calculate speed
	double time_passed = (current_time - data_time) / 1000; // Seconds
	double distance_enemy_fast_tank_traveled = calculate_distance_between_two_coordinates(enemy_fast_x, enemy_fast_y, data_enemy_fast_x, data_enemy_fast_y);
	double distance_enemy_slow_tank_traveled = calculate_distance_between_two_coordinates(enemy_slow_x, enemy_slow_y, data_enemy_slow_x, data_enemy_slow_y);

	// Bug fix
	distance_enemy_fast_tank_traveled = (distance_enemy_fast_tank_traveled > 20) ? 0 : distance_enemy_fast_tank_traveled;
	distance_enemy_slow_tank_traveled = (distance_enemy_slow_tank_traveled > 20) ? 0 : distance_enemy_slow_tank_traveled;

	//enemy_data[9] = enemy_data[5];
	//enemy_data[10] = enemy_data[6];

	enemy_data[5] = distance_enemy_fast_tank_traveled / time_passed;
	enemy_data[6] = distance_enemy_slow_tank_traveled / time_passed;
	//enemy_data[5] = (enemy_data[5] + enemy_data[9]) / 2;
	//enemy_data[6] = (enemy_data[6] + enemy_data[10]) / 2;

	//enemy_data[11] = enemy_data[7];
	//enemy_data[12] = enemy_data[8];

	// Calculate enemy direction
	enemy_data[7] = calculate_relative_radial_position(data_enemy_fast_x, data_enemy_fast_y, enemy_fast_x, enemy_fast_y);
	enemy_data[8] = calculate_relative_radial_position(data_enemy_slow_x, data_enemy_slow_y, enemy_slow_x, enemy_slow_y);
	//enemy_data[7] = (enemy_data[7] + enemy_data[11]) / 2;
	//enemy_data[8] = (enemy_data[8] + enemy_data[12]) / 2;

	enemy_data[0] = current_time;
	enemy_data[1] = enemy_fast_x;
	enemy_data[2] = enemy_fast_y;
	enemy_data[3] = enemy_slow_x;
	enemy_data[4] = enemy_slow_y;
}

bool our_fast_tank_attack_best_target(zmq::socket_t& command_channel, json::Object game_state, string client_token, vector<double>& enemy_data)
{
	// Get our fast tank position
	double our_x, our_y;
	our_fast_tank_position(game_state) >> our_x >> our_y;

	// Determine best enemy target
	string target_type, type;
	double enemy_x, enemy_y;
	determine_coordinates_of_best_target(game_state, our_x, our_y) >> enemy_x >> enemy_y >> target_type >> type;

	// Calculate target lead  
	double speed = (target_type == "fast") ? enemy_data[5] : enemy_data[6];
	double direction = (target_type == "fast") ? enemy_data[7] : enemy_data[8]; // Degrees
	double t = 0;
	double temp_x = enemy_x;
	double temp_y = enemy_y;

	for (int i = 0; i <= 100; i++)
	{
		t = calculate_distance_between_two_coordinates(our_x, our_y, temp_x, temp_y) / 30;
		temp_x = enemy_x + speed * cos(direction / 180 * pi) * t;
		temp_y = enemy_y + speed * sin(direction / 180 * pi) * t;
	}

	string enemy_name = get_enemy_name();
	if (enemy_name == "jnielson" || enemy_name == "Bit Brigade" || enemy_name == "Think Tank" || enemy_name == "ThinkTanks" || enemy_name == "testclient" || enemy_name == "The Nunchucks" || enemy_name == "One Lightning Trail" || enemy_name == "Byte Me")
	{
	}
	else
	{
		enemy_x = temp_x;
		enemy_y = temp_y;
	}

	if (is_trajectory_blocked(our_x, our_y, enemy_x, enemy_y))
	{
		type = "point";
	}

	// Calculate the enemy position relative to ours in degrees
	double enemy_radial_position = calculate_relative_radial_position(our_x, our_y, enemy_x, enemy_y); // Degrees

	// Determine our current turret direction
	double our_turret_direction = our_fast_tank_turret_direction(game_state); // Degrees

	// Calculate the optimal turret rotation distance and direction in degrees
	double new_turret_direction = calculate_optimal_rotation(our_turret_direction, enemy_radial_position); // Degrees

	// Point turret to the best turret direction and shoot!
	double d = calculate_distance_between_two_coordinates(our_x, our_y, enemy_x, enemy_y);
	double margin_of_error;

	if (d < 10)
	{
		margin_of_error = 5;
	}
	else if (d < 20)
	{
		margin_of_error = 2;
	}
	else if (d >= 20)
	{
		margin_of_error = 0.5;
	}

	// Point chassis to enemy if we can shoot them
	if (type == "shoot" && d < 20)
	{
		double our_chassis_direction = our_fast_tank_direction(game_state);
		double new_chassis_direction = calculate_optimal_rotation(our_chassis_direction, enemy_radial_position);
		our_fast_tank_rotate(command_channel, game_state, client_token, new_chassis_direction);
		our_fast_tank_move(command_channel, game_state, client_token, -3);
	}

	if (abs(new_turret_direction) > margin_of_error)
	{
		our_fast_tank_rotate_turret(command_channel, game_state, client_token, new_turret_direction); // Rotate turret
		our_fast_tank_stop(command_channel, game_state, client_token, "FIRE");
	}
	else
	{
		if (type == "shoot")
		{
			// Prevent friendly fire
			if (is_our_slow_tank_alive(game_state))
			{
				// Calculate where the projectile will be when it reaches the same radius as our slow tank
				double slow_x, slow_y;
				our_slow_tank_position(game_state) >> slow_x >> slow_y;
				double t = calculate_distance_between_two_coordinates(our_x, our_y, slow_x, slow_y) / 30;
				double target_x = our_x + 30 * cos(our_turret_direction / 180 * pi) * t;
				double target_y = our_y + 30 * sin(our_turret_direction / 180 * pi) * t;

				if (calculate_distance_between_two_coordinates(target_x, target_y, slow_x, slow_y) > 3)
				{
					our_fast_tank_shoot(command_channel, game_state, client_token); // FIRE
				}
				else
				{
					our_fast_tank_stop(command_channel, game_state, client_token, "FIRE");
				}
			}
			else
			{
				our_fast_tank_shoot(command_channel, game_state, client_token); // FIRE
			}
		}
	}
	return (type == "shoot");
}

bool our_slow_tank_attack_best_target(zmq::socket_t& command_channel, json::Object game_state, string client_token, vector<double>& enemy_data)
{
	// Get our fast tank position
	double our_x, our_y;
	our_slow_tank_position(game_state) >> our_x >> our_y;

	// Determine position of best enemy target
	string target_type, type;
	double enemy_x, enemy_y;
	determine_coordinates_of_best_target(game_state, our_x, our_y) >> enemy_x >> enemy_y >> target_type >> type;

	// Calculate target lead 
	double speed = (target_type == "fast") ? enemy_data[5] : enemy_data[6];
	double direction = (target_type == "fast") ? enemy_data[7] : enemy_data[8];
	double t = 0;
	double temp_x = enemy_x;
	double temp_y = enemy_y;

	for (int i = 0; i <= 100; i++)
	{
		t = calculate_distance_between_two_coordinates(our_x, our_y, temp_x, temp_y) / 30;
		temp_x = enemy_x + speed * cos(direction / 180 * pi) * t;
		temp_y = enemy_y + speed * sin(direction / 180 * pi) * t;
	}

	string enemy_name = get_enemy_name();
	if (enemy_name == "jnielson" || enemy_name == "Bit Brigade" || enemy_name == "Think Tank" || enemy_name == "ThinkTanks" || enemy_name == "testclient" || enemy_name == "The Nunchucks" || enemy_name == "One Lightning Trail" || enemy_name == "Byte Me")
	{
	}
	else
	{
		enemy_x = temp_x;
		enemy_y = temp_y;
	}

	if (is_trajectory_blocked(our_x, our_y, enemy_x, enemy_y))
	{
		type = "point";
	}

	// Calculate the enemy position relative to ours in degrees
	double enemy_radial_position = calculate_relative_radial_position(our_x, our_y, enemy_x, enemy_y); // Degrees

	// Determine our current turret direction
	double our_turret_direction = our_slow_tank_turret_direction(game_state); // Degrees

	// Calculate the optimate turret rotation distance and direction in degrees
	double new_turret_direction = calculate_optimal_rotation(our_turret_direction, enemy_radial_position); // Degrees

	// Point turret to the best turret direction and shoot!
	double d = calculate_distance_between_two_coordinates(our_x, our_y, enemy_x, enemy_y);
	double margin_of_error;

	if (d < 10)
	{
		margin_of_error = 5;
	}
	else if (d < 20)
	{
		margin_of_error = 2;
	}
	else if (d >= 20)
	{
		margin_of_error = 0.5;
	}

	// Point chassis to enemy if we can shoot them
	if (type == "shoot" && d < 40)
	{
		double our_chassis_direction = our_slow_tank_direction(game_state);
		double new_chassis_direction = calculate_optimal_rotation(our_chassis_direction, enemy_radial_position);
		our_slow_tank_rotate(command_channel, game_state, client_token, new_chassis_direction);
		our_slow_tank_move(command_channel, game_state, client_token, -3);
	}

	if (abs(new_turret_direction) > margin_of_error)
	{
		our_slow_tank_rotate_turret(command_channel, game_state, client_token, new_turret_direction); // Rotate turret
		our_slow_tank_stop(command_channel, game_state, client_token, "FIRE");
	}
	else
	{
		if (type == "shoot")
		{
			// Prevent friendly fire
			if (is_our_fast_tank_alive(game_state))
			{
				// Calculate where the projectile will be when it reaches the same radius as our slow tank
				double fast_x, fast_y;
				our_fast_tank_position(game_state) >> fast_x >> fast_y;
				double t = calculate_distance_between_two_coordinates(our_x, our_y, fast_x, fast_y) / 30;
				double target_x = our_x + 30 * cos(our_turret_direction / 180 * pi) * t;
				double target_y = our_y + 30 * sin(our_turret_direction / 180 * pi) * t;

				if (calculate_distance_between_two_coordinates(target_x, target_y, fast_x, fast_y) > 3)
				{
					our_slow_tank_shoot(command_channel, game_state, client_token); // FIRE
				}
				else
				{
					our_slow_tank_stop(command_channel, game_state, client_token, "FIRE");
				}
			}
			else
			{
				our_slow_tank_shoot(command_channel, game_state, client_token); // FIRE
			}
		}
	}
	return (type == "shoot");
}

bool our_fast_tank_move_to_coordinates(zmq::socket_t& command_channel, json::Object game_state, string client_token, double desired_x, double desired_y, string direction)
{
	// Get our fast tank position
	double our_x, our_y;
	our_fast_tank_position(game_state) >> our_x >> our_y;

	// Only if we are NOT already at the desired coordinates
	double distance = calculate_distance_between_two_coordinates(our_x, our_y, desired_x, desired_y);
	if (distance > 2)
	{
		// Calculate what direction and how far to turn so that we face directly at the desired coordinates
		double current_tank_direction = our_fast_tank_direction(game_state);
		double radial_position = calculate_relative_radial_position(our_x, our_y, desired_x, desired_y);
		double new_tank_direction = calculate_optimal_rotation(current_tank_direction, radial_position);

		// Move
		our_fast_tank_rotate(command_channel, game_state, client_token, new_tank_direction);
		our_fast_tank_move(command_channel, game_state, client_token, (direction == "FWD") ? 3 : -3);

		return true;
	}
	return false;
}

bool our_slow_tank_move_to_coordinates(zmq::socket_t& command_channel, json::Object game_state, string client_token, double desired_x, double desired_y, string direction)
{
	// Get our slow tank position
	double our_x, our_y;
	our_slow_tank_position(game_state) >> our_x >> our_y;

	// Only if we are NOT already at the desired coordinates
	double distance = calculate_distance_between_two_coordinates(our_x, our_y, desired_x, desired_y);
	if (distance > 2)
	{
		// Calculate what direction and how far to turn so that we face directly at the desired coordinates
		double current_tank_direction = our_slow_tank_direction(game_state);
		double radial_position = calculate_relative_radial_position(our_x, our_y, desired_x, desired_y);
		double new_tank_direction = calculate_optimal_rotation(current_tank_direction, radial_position);

		// Move
		our_slow_tank_rotate(command_channel, game_state, client_token, new_tank_direction);
		our_slow_tank_move(command_channel, game_state, client_token, (direction == "FWD") ? 3 : -3);

		return true;
	}
	return false;
}

void our_fast_tank_follow_path(zmq::socket_t& command_channel, json::Object game_state, string client_token)
{
	if (get_current_map_name() == "  ") return;

	// Get our fast tank position
	int our_x, our_y;
	our_fast_tank_position(game_state) >> our_x >> our_y;

	// get path index
	int path_index = get_our_fast_tank_path_index();

	// If path index is -1 then find the nearest path index
	if (path_index == -1)
	{
		path_index = determine_nearest_path_index(our_x, our_y);
	}

	// Exceptions
	if (get_current_map_name() == "River_runs_through_it" || get_current_map_name() == "River_almost_runs_through_it" || get_current_map_name() == "Vertical_river")
	{
		if (path_index == get_our_slow_tank_path_index())
		{
			if (path_index == 0)
			{
				path_index = 1;
			}
			else if (path_index == 1)
			{
				path_index = 0;
			}
			else if (path_index == 2)
			{
				path_index = 3;
			}
			else if (path_index == 3)
			{
				path_index = 2;
			}
		}
	}

	set_our_fast_tank_path_index(path_index);

	// Get the path coordinate using the index
	double path_x, path_y;
	get_path_coordinate(path_index) >> path_x >> path_y;

	// Try and move to the path coordinates
	if (!our_fast_tank_move_to_coordinates(command_channel, game_state, client_token, path_x, path_y, "FWD"))
	{
		// Exceptions
		if (get_current_map_name() == "River_runs_through_it" || get_current_map_name() == "River_almost_runs_through_it" || get_current_map_name() == "Vertical_river")
		{
			return;
		}

		// When we arrive at the coordinates move on to the next path index
		set_our_fast_tank_path_index(path_index + 1);
		return;
	}
}

void our_slow_tank_follow_path(zmq::socket_t& command_channel, json::Object game_state, string client_token)
{
	if (get_current_map_name() == "  ") return;

	// Get our slow tank position
	int our_x, our_y;
	our_slow_tank_position(game_state) >> our_x >> our_y;

	// Get path index
	int path_index = get_our_slow_tank_path_index();

	// If path index is -1 then find the nearest path index
	if (path_index == -1)
	{
		path_index = determine_nearest_path_index(our_x, our_y);
	}

	// Exceptions
	if (get_current_map_name() == "River_runs_through_it" || get_current_map_name() == "River_almost_runs_through_it" || get_current_map_name() == "Vertical_river")
	{
		if (path_index == get_our_fast_tank_path_index())
		{
			if (path_index == 0)
			{
				path_index = 1;
			}
			else if (path_index == 1)
			{
				path_index = 0;
			}
			else if (path_index == 2)
			{
				path_index = 3;
			}
			else if (path_index == 3)
			{
				path_index = 2;
			}
		}
	}

	set_our_slow_tank_path_index(path_index);

	// Get the path coordinate
	double path_x, path_y;
	get_path_coordinate(path_index) >> path_x >> path_y;

	// Try and move to the path coordinates
	if (!our_slow_tank_move_to_coordinates(command_channel, game_state, client_token, path_x, path_y, "FWD"))
	{
		// Exceptions
		if (get_current_map_name() == "River_runs_through_it" || get_current_map_name() == "River_almost_runs_through_it" || get_current_map_name() == "Vertical_river")
		{
			return;
		}

		// When we arrive at the coordinates move on to the next path index
		set_our_slow_tank_path_index(path_index + 1);
		return;
	}
}

bool our_fast_tank_dodge(zmq::socket_t& command_channel, json::Object game_state, string client_token)
{
	// Get a list of all projectiles
	vector<vector<double>> projectiles = all_projectiles(game_state);

	// Check if there are no projectiles
	if (projectiles.empty())
	{
		// Nothing to dodge
		return false;
	}

	// Get our position
	double our_x, our_y;
	our_fast_tank_position(game_state) >> our_x >> our_y;

	// Get our current direction
	double tank_direction = our_fast_tank_direction(game_state);

	// Look at all enemy projecties, check if they will hit, and then find the nearest one
	stringstream nearest_projectile;
	string type;
	double nearest_distance = 1000000;
	for (vector<double> projectile : projectiles)
	{
		// Get projectile information
		double x = projectile[0];
		double y = projectile[1];
		double direction = projectile[2];

		// Calculate where the projectile will be when it reaches the same radius as this tank
		double t = calculate_distance_between_two_coordinates(our_x, our_y, x, y) / 30;
		double target_x = x + 30 * cos(direction) * t;
		double target_y = y + 30 * sin(direction) * t;

		// Check if it will hit this tank
		if (calculate_distance_between_two_coordinates(our_x, our_y, target_x, target_y) <= 4)
		{
			if (!is_trajectory_blocked(x, y, our_x, our_y))
			{
				// If it does hit calculate its current distance from this tank
				double distance = calculate_distance_between_two_coordinates(our_x, our_y, x, y);
				// Decide if it is the closest projectile
				if (distance < nearest_distance)
				{
					// Save the closest projectile
					nearest_projectile << x << " " << y << " " << direction;
					nearest_distance = distance;
					type = "FWD";
				}
			}
		}

		double dx = 1.0 * cos(tank_direction / 180 * pi);
		double dy = 1.0 * sin(tank_direction / 180 * pi);
		t = calculate_distance_between_two_coordinates(our_x + dx, our_y + dy, x, y) / 30;
		target_x = x + 30 * cos(direction) * t;
		target_y = y + 30 * sin(direction) * t;

		// Check if it will hit this tank
		if (calculate_distance_between_two_coordinates(our_x + dx, our_y + dy, target_x, target_y) <= 4)
		{
			if (!is_trajectory_blocked(x, y, our_x + dx, our_y + dy))
			{
				// If it does hit calculate its current distance from this tank
				double distance = calculate_distance_between_two_coordinates(our_x + dx, our_y + dy, x, y);
				// Decide if it is the closest projectile
				if (distance < nearest_distance)
				{
					// Save the closest projectile
					nearest_projectile << x << " " << y << " " << direction;
					nearest_distance = distance;
					type = "REV";
				}
			}
		}
	}

	// Check if there is anything to dodge
	if (abs(nearest_distance - 1000000) < 0.0000001)
	{
		// Nothing to dodge
		return false;
	}

	// Get projectile information
	double x, y, direction;
	nearest_projectile >> x >> y >> direction;
	direction = direction / pi * 180; // Convert to degrees

	// Possible dodge directions
	double possible_dodge_direction[4];
	possible_dodge_direction[0] = fix_angle(direction + 45);
	possible_dodge_direction[1] = fix_angle(direction - 45);
	possible_dodge_direction[2] = fix_angle(direction + 90);
	possible_dodge_direction[3] = fix_angle(direction - 90);

	// Decide to dodge left or dodge right and then dodge
	double best_dodge_direction = possible_dodge_direction[0];
	double best_dodge_distance = calculate_optimal_rotation(tank_direction, possible_dodge_direction[0]);

	for (int i = 1; i < 4; i++)
	{
		double dodge_distance = calculate_optimal_rotation(tank_direction, possible_dodge_direction[i]);
		if (abs(dodge_distance) < abs(best_dodge_distance))
		{
			best_dodge_distance = dodge_distance;
			best_dodge_direction = possible_dodge_direction[i];
		}
	}

	our_x = our_x + 3 * cos(best_dodge_direction * pi / 180);
	our_y = our_y + 3 * sin(best_dodge_direction * pi / 180);
	our_fast_tank_move_to_coordinates(command_channel, game_state, client_token, our_x, our_y, type);

	return true;
}

bool our_slow_tank_dodge(zmq::socket_t& command_channel, json::Object game_state, string client_token)
{
	// Get a list of all projectiles
	vector<vector<double>> projectiles = all_projectiles(game_state);

	// Check if there are no projectiles
	if (projectiles.empty())
	{
		// Nothing to dodge
		return false;
	}

	// Get our position
	double our_x, our_y;
	our_slow_tank_position(game_state) >> our_x >> our_y;

	// Get our current direction
	double tank_direction = our_slow_tank_direction(game_state);

	// Look at all enemy projecties, check if they will hit, and then find the nearest one
	stringstream nearest_projectile;
	string type;
	double nearest_distance = 1000000;
	for (vector<double> projectile : projectiles)
	{
		// Get projectile information
		double x = projectile[0];
		double y = projectile[1];
		double direction = projectile[2];

		// Calculate where the projectile will be when it reaches the same radius as this tank
		double t = calculate_distance_between_two_coordinates(our_x, our_y, x, y) / 30;
		double target_x = x + 30 * cos(direction) * t;
		double target_y = y + 30 * sin(direction) * t;

		// Check if it will hit this tank
		if (calculate_distance_between_two_coordinates(our_x, our_y, target_x, target_y) <= 4)
		{
			if (!is_trajectory_blocked(x, y, our_x, our_y))
			{
				// If it does hit calculate its current distance from this tank
				double distance = calculate_distance_between_two_coordinates(our_x, our_y, x, y);
				// Decide if it is the closest projectile
				if (distance < nearest_distance)
				{
					// Save the closest projectile
					nearest_projectile << x << " " << y << " " << direction;
					nearest_distance = distance;
					type = "FWD";
				}
			}
		}

		double dx = 0.5 * cos(tank_direction / 180 * pi);
		double dy = 0.5 * sin(tank_direction / 180 * pi);
		t = calculate_distance_between_two_coordinates(our_x + dx, our_y + dy, x, y) / 30;
		target_x = x + 30 * cos(direction) * t;
		target_y = y + 30 * sin(direction) * t;

		// Check if it will hit this tank
		if (calculate_distance_between_two_coordinates(our_x + dx, our_y + dy, target_x, target_y) <= 4)
		{
			if (!is_trajectory_blocked(x, y, our_x + dx, our_y + dy))
			{
				// If it does hit calculate its current distance from this tank
				double distance = calculate_distance_between_two_coordinates(our_x + dx, our_y + dy, x, y);
				// Decide if it is the closest projectile
				if (distance < nearest_distance)
				{
					// Save the closest projectile
					nearest_projectile << x << " " << y << " " << direction;
					nearest_distance = distance;
					type = "REV";
				}
			}
		}
	}

	// Check if there is anything to dodge
	if (abs(nearest_distance - 1000000) < 0.0000001)
	{
		// Nothing to dodge
		return false;
	}

	// Get projectile information
	double x, y, direction;
	nearest_projectile >> x >> y >> direction;
	direction = direction / pi * 180; // Convert to degrees

	// Possible dodge directions
	double possible_dodge_direction[4];
	possible_dodge_direction[0] = fix_angle(direction + 45);
	possible_dodge_direction[1] = fix_angle(direction - 45);
	possible_dodge_direction[2] = fix_angle(direction + 90);
	possible_dodge_direction[3] = fix_angle(direction - 90);

	// Decide to dodge left or dodge right and then dodge
	double best_dodge_direction = possible_dodge_direction[0];
	double best_dodge_distance = calculate_optimal_rotation(tank_direction, possible_dodge_direction[0]);

	for (int i = 1; i < 4; i++)
	{
		double dodge_distance = calculate_optimal_rotation(tank_direction, possible_dodge_direction[i]);
		if (abs(dodge_distance) < abs(best_dodge_distance))
		{
			best_dodge_distance = dodge_distance;
			best_dodge_direction = possible_dodge_direction[i];
		}
	}

	our_x = our_x + 3 * cos(best_dodge_direction * pi / 180);
	our_y = our_y + 3 * sin(best_dodge_direction * pi / 180);
	our_slow_tank_move_to_coordinates(command_channel, game_state, client_token, our_x, our_y, type);

	return true;
}